/**
 * Collaborative Story Development Web Application (CSDApp) Copyright Anton
 * Strack 2014
 *
 * This file is part of Collaborative Story Development Application (CSDApp).
 *
 * CSDApp is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or any later version.
 *
 * CSDApp is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * CSDApp. If not, see <http://www.gnu.org/licenses/>.
 *
 */
package csdwa;

import java.sql.Connection;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * This class handles the core functional logic of the history system.
 *
 * @author Anton Strack
 */
public class UserHistoryManager<E extends CsdwaRecordable> {

    /**
     * The datasource used for getting the connection to the database
     */
    private Connection conn;

    /**
     * The default constructor for the user history manager
     *
     * @param conn
     */
    public UserHistoryManager(Connection conn) {
        this.conn = conn;
    }

    /**
     * This method records the user's history regarding a specific resource. If
     * this is the first time the user interacted with the resource then a new
     * history row is inserted into the database, otherwise the history row for
     * the user and resource is updated to always include the current date and
     * also the state of the resource if it has changed.
     *
     * @param user the user performing the action to record the history of
     * @param actionId the action the user is performing on the resource
     * @param post the resource being acted on by the user.
     */
    public void recordUserPostHistory(User user, int actionId, CsdwaRecordable post) {
        // check if there is a a posthistory for the post already
        UserHistoryDao phdao = new UserHistoryDao(conn);
        phdao.selectUserPostHistory(user.getId(), actionId, post.getResourceTypeId(), post.getId());
        UserHistory posthistory = phdao.getBean();
        Timestamp time = new Timestamp(new Date().getTime());
        if (posthistory != null) {
            posthistory.setHistoryDate(time);
            phdao.COLUMNS_TO_UPDATE = "HistoryDate";
            if (post.getStateId() != posthistory.getPostStateId()) {
                posthistory.setPostStateId(post.getStateId());
                phdao.COLUMNS_TO_UPDATE += ", PostStateID";
            }
            if (post.getName().equals(posthistory.getPostName()) == false) {
                posthistory.setPostName(post.getName());
                phdao.COLUMNS_TO_UPDATE += ", PostName";
            }
            int result = phdao.updateBean(posthistory);
        } else {
            posthistory = ResourceFactory.makePostHistory();
            posthistory.setActionTypeId(actionId);
            posthistory.setHistoryDate(time);
            posthistory.setPostId(post.getId());
            posthistory.setPostName(post.getName());
            posthistory.setPostResourceTypeId(post.getResourceTypeId());
            posthistory.setPostStateId(post.getStateId());
            posthistory.setUserId(user.getId());
            posthistory.setUserName(user.getName());
            int result = phdao.insertBean(posthistory);
        }
    }

//    /**
//     * This method checks the user's history
//     *
//     * @param user
//     * @param actionId
//     * @param post
//     * @return
//     */
//    public int checkUserPostHistory(User user, int actionId, CsdwaRecordable post) {
//        int historyState = 1; // unread
//        UserHistoryDao phdao = new UserHistoryDao(ds.getConnection());
//        phdao.setMarker(QueryMarker.CORE_CONDITIONS, "posthistory.PostResourceTypeID = ? AND posthistory.ActionTypeID = ? AND posthistory.UserID = ? AND posthistory.PostID = ?");
//        phdao.selectBeans();
//        UserHistory posthistory = phdao.getBean();
//        if (posthistory != null) {
//            // if post history timestamp is after or equal to post updated timestamp then set the state as read
//            if (posthistory.getHistoryDate().compareTo(post.getUpdatedDate()) >= 0) {
//                historyState = 2;
//            }
//        }
//        return historyState;
//    }
    /**
     * This method sets the flag on the CsdwaRecordable resource indicating the
     * user's interaction state between them and a resource. It defaults to
     * being unread for all resources. This method is a wrapper for the
     * processUserPostHistory(List<CsdwaRecordable> posts, User user){} method
     *
     * @param request the request object containing a list of beans accessible
     * by request.getBeans();
     * @return the request object with the updated bean list with each bean
     * containing a derivedValue with a key of "historyState" mapping to the
     * state id of the resource.
     */
    public CsdwaRequest processUserPostHistory(CsdwaRequest request) {
        List<E> posts = (List<E>) request.getBeans();
        User user = request.getCurrentUser();
        List<E> processedPosts = this.processUserPostHistory(posts, user);

        request.setBeans(processedPosts);
        return request;
    }

    /**
     * This method sets the flag on the CsdwaRecordable resource indicating the
     * user's interaction state between them and a resource. It defaults to
     * being unread for all resources.
     *
     * @param posts a list of beans to process.
     * @param user the user to process the post history against.
     *
     * @return the updated bean list with each bean containing a derivedValue
     * with a key of "historyState" mapping to the state id of the resource.
     */
    public List<E> processUserPostHistory(List<E> posts, User user) {
        //  List<CsdwaRecordable> processedPosts = new ArrayList<CsdwaRecordable>();
        if (posts != null && posts.isEmpty() == false) {
            InfoAggregator infoAggregator = InfoAggregator.getInstance();
            UserHistoryDao phdao = new UserHistoryDao(conn);
            int postResourceId = posts.get(0).getResourceTypeId();
            int userId = user.getId();
            int actionId = infoAggregator.getActionTypeId("view"); // need to find a better solution
            int result = phdao.selectUserPostHistories(userId, actionId, postResourceId);
            if (result > 0) {
                CommentDao cDao = new CommentDao(conn);
                String max = "LIMIT " + Configuration.getRecentCommentMaxCount();
                cDao.setMarker(QueryMarker.LIMIT, max);
                cDao.addUsingQueryMarker(QueryMarker.LIMIT);
                result = cDao.selectBeansByParentType(postResourceId);
                List<Comment> comments;
                if (result > 0) {
                    comments = cDao.getBeans();
                } else {
                    comments = new ArrayList<Comment>();// empty array list.
                }
                List<UserHistory> posthistories = phdao.getBeans();
                for (CsdwaRecordable post : posts) {
                    String unreadState = infoAggregator.getStateTypeId("unread") + "";// convert to string
                    String commentedState = infoAggregator.getStateTypeId("commentedon") + "";// convert to string
                    String readState = infoAggregator.getStateTypeId("read") + "";// convert to string
                    post.setDerivedValue("historyState", unreadState); // default to unread
                    for (UserHistory posthistory : posthistories) {
                        if (posthistory.getPostId() == post.getId()) {
                            if (posthistory.getHistoryDate().compareTo(post.getUpdatedDate()) >= 0) {
                                post.setDerivedValue("historyState", readState);
                            }
                            // if there are comments to check, and the post isn't already set as unread, then check if it has been commented on.
                            if (result > 0 && posthistory.getHistoryDate().compareTo(post.getUpdatedDate()) >= 0) {
                                // look through comments to see if one has a greater post date then the history date.
                                for (Comment comment : comments) {
                                    if (comment.getParentId() == post.getId() && posthistory.getHistoryDate().compareTo(comment.getUpdatedDate()) < 0) {
                                        post.setDerivedValue("historyState", commentedState);
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    // processedPosts.add(post);
                }
            }
        }
        return posts;
    }

    /**
     * This method is used to attach the recent user interaction history to the
     * request so it is available to the jsp pages
     *
     * @param request the request to add the user histories to.
     * @return the request object containing the user histories with key
     * "userHistories"
     */
    public CsdwaRequest attachUserPostHistories(CsdwaRequest request) {
        UserHistoryDao phdao = new UserHistoryDao(conn);
        User user = request.getCurrentUser();
        String max = "LIMIT " + Configuration.getRecentHistoryMaxCount();
        phdao.setMarker(QueryMarker.LIMIT, max);
        phdao.addUsingQueryMarker(QueryMarker.LIMIT);
        phdao.setUseRelatedMarkers(true);
        phdao.selectUserPostHistories(user.getId());
        request.setAttribute("userHistories", phdao.getBeans());
        return request;
    }
}
